﻿
// 2020/8/26: 首个记录（根据env-sample-v2.h 2020/5/15版本修改）
// 2020/11/3: 新增气压、相对湿度、能见度、风向、风速、PM10、道路状况、车道交通状况、灾害情况等字段。道路交通状况更名
// 2020/11/12: 支持Spadas7
// 2020/11/26: 实现fromGeneralSample
// 2022/11/2: 支持Spadas8.4
// 2023/8/17: 新增瞬时雨量字段
// 2023/10/10: 补充部分英文注释
// 2023/10/24: 新增字段（累计雨量、精确风向、总辐射强度、紫外线辐射强度、臭氧浓度、一氧化碳浓度、二氧化硫浓度、二氧化氮浓度）

#ifndef ENV_SAMPLE_V3_H
#define ENV_SAMPLE_V3_H

#include "spadas.h"

namespace env_sample_v3
{
	using namespace spadas;

	enum WeatherType
	{
		WT_Unknown = 0, // 未知
		WT_Sunny = 1, // 晴
		WT_Cloudy = 2, // 阴
		WT_Rainy = 3, // 雨
		WT_Snowy = 4, // 雪
		WT_Foggy = 5, // 雾
		WT_Sand = 6, // 沙尘
	};

	enum RoadType
	{
		RT_Unknown = 0, // 未知
		RT_Highway = 1, // Without traffic light / 高速路，无红绿灯
		RT_CityExpress = 2, // Without traffic light / 城市快速路，无红绿灯
		RT_CityMain = 3, // With traffic light / 城市主干道，有红绿灯
		RT_GeneralRoad = 4, // With traffic light / 一般道路，有红绿灯
		RT_CountryRoad = 5, // Without traffic light / 乡村道路，无红绿灯
	};

	enum SpotType
	{
		ST_Unknown = 0, // 未知
		ST_TollGate = 1, // 收费站
		ST_ServiceArea = 2, // 服务区
		ST_Tunnel = 3, // 隧道
		ST_Accident = 4, // 事故
		ST_Crossing = 5, // Intersection / 路口
		ST_Roundabout = 7, // 环岛
		ST_HighwayEntrance = 8, // 高速入口
		ST_HighwayExit = 9, // 高速出口
		ST_Bridge = 10, // 桥
		ST_ExpressEntrance = 11, // 快速路入口
		ST_ExpressExit = 12, // 快速路出口
	};

	struct Spot
	{
		SpotType spotType; // 场所类型
		Double distance; // [m] Distance between the spot and subject vehicle / 本车到场所的距离
	};

	enum TrafficStatus
	{
		TF_Unknown = 0, // 未知
		TF_Open = 1, // 畅通
		TF_Slow = 2, // 缓慢
		TF_Crowded = 3, // 拥挤
		TF_Reverse = 4, // Only for adjacent lane traffic / 逆向（仅限相邻车道交通状况）
	};

	enum WindDirection
	{
		WD_Unknown = 0, // 未知
		WD_East = 1, // 东风
		WD_South = 2, // 南风
		WD_West = 3, // 西风
		WD_North = 4, // 北风
		WD_SouthEast = 5, // 东南风
		WD_NorthEast = 6, // 东北风
		WD_SouthWest = 7, // 西南风
		WD_NorthWest = 8, // 西北风
	};

	enum RoadSurfaceStatus
	{
		RSS_Unknown = 0, // 未知
		RSS_Dry = 1, // 干燥
		RSS_Wet = 2, // 湿滑
		RSS_SnowCovered = 3, // 积雪
		RSS_Frozen = 4, // 结冰
	};

	enum DisasterStatus
	{
		DS_Unknown = 0, // 未知
		DS_No = 1, // 无灾害
		DS_Yes = 2, // 有灾害
	};

	struct EnvSample
	{
		FullTimestamp timestamp; // 时间戳

		WeatherType weatherType; // 天气类型
		OptionalDouble temperature; // [°C] 温度
		OptionalDouble humidity; // [%] Relative humidity / 相对湿度
		OptionalDouble pressure; // [mb] Atmospheric pressure / 气压
		OptionalDouble pm25; // [ug/m3] PM 2.5
		OptionalDouble pm10; // [ug/m3] PM 10
		OptionalDouble o3Concentration; // [ppb] Ozone concentration / 臭氧浓度
		OptionalDouble coConcentration; // [ppb] Carbon monoxide concentration / 一氧化碳浓度
		OptionalDouble so2Concentration; // [ppb] Sulfur dioxide concentration / 二氧化硫浓度
		OptionalDouble no2Concentration; // [ppb] Nitrogen dioxide concentration / 二氧化氮浓度
		OptionalDouble visibility; // [KM] 能见度
		OptionalDouble illumination; // [lux] 光照强度
		OptionalDouble totalRadiationIntensity; // [W/m2] 总辐射强度
		OptionalDouble ultravioletRadiationIntensity; // [W/m2] 紫外线辐射强度
		OptionalDouble rainfall; // [mm/min] Instantaneous rainfall / 瞬时雨量
		OptionalDouble cumulativeRainfall; // [mm] 累计雨量
		OptionalDouble windSpeed; // [m/s] 风速
		OptionalDouble accurateWindDirection; // [deg] Accurate wind direction (from), 0 is north wind, CCW is positive, ranges -180~180 / 北风为0，CCW为正，范围在-180～180度
		WindDirection windDirection; // Coarse wind direction (from) / 大致风向
		DisasterStatus typhoonStatus; // 台风状况
		DisasterStatus tornadoStatus; // 龙卷风状况
		DisasterStatus lightningStatus; // 雷电状况
		DisasterStatus hailStatus; // 冰雹状况

		String roadName; // 道路名称
		OptionalInt speedLimit; // [KPH] 道路限速
		RoadType roadType; // 道路类型
		RoadSurfaceStatus roadSurfaceStatus; // 路面状况
		TrafficStatus roadTraffic; // 道路交通状况
		TrafficStatus currentLaneTraffic; // 本车道交通状况
		TrafficStatus leftLaneTraffic; // 左车道交通状况
		TrafficStatus rightLaneTraffic; // 右车道交通状况
		Array<Spot> spots; // 场所列表

		EnvSample()
		{
			weatherType = WT_Unknown;
			windDirection = WD_Unknown;
			typhoonStatus = DS_Unknown;
			tornadoStatus = DS_Unknown;
			lightningStatus = DS_Unknown;
			hailStatus = DS_Unknown;
			roadType = RT_Unknown;
			roadSurfaceStatus = RSS_Unknown;
			roadTraffic = TF_Unknown;
			currentLaneTraffic = TF_Unknown;
			leftLaneTraffic = TF_Unknown;
			rightLaneTraffic = TF_Unknown;
		}

		static String getProtocolName()
		{
			return "env-sample-v3";
		}

		static Bool supportInterpolation()
		{
			return FALSE;
		}

		SessionGeneralSample toGeneralSample()
		{
			SessionGeneralSample sample;
			sample.timestamp = timestamp;
			sample.values = Array<GeneralElement>(64 + spots.size() * 2);

			sample.values[1] = spots.size();

			sample.values[2] = (UInt)weatherType;
			sample.values[3] = temperature;
			sample.values[4] = pm25;
			sample.values[5] = speedLimit.valid ? speedLimit.value : OptionalDouble();
			sample.values[6] = (UInt)roadType;
			sample.values[7] = illumination;
			sample.values[8] = windSpeed;
			sample.values[9] = (UInt)roadTraffic;
			sample.values[10] = roadName;
			sample.values[11] = pressure;
			sample.values[12] = humidity;
			sample.values[13] = visibility;
			sample.values[14] = (UInt)windDirection;
			sample.values[15] = pm10;
			sample.values[16] = (UInt)roadSurfaceStatus;
			sample.values[17] = (UInt)currentLaneTraffic;
			sample.values[18] = (UInt)leftLaneTraffic;
			sample.values[19] = (UInt)rightLaneTraffic;
			sample.values[20] = (UInt)typhoonStatus;
			sample.values[21] = (UInt)tornadoStatus;
			sample.values[22] = (UInt)lightningStatus;
			sample.values[23] = (UInt)hailStatus;
			sample.values[24] = rainfall;
			sample.values[25] = cumulativeRainfall;
			sample.values[26] = accurateWindDirection;
			sample.values[27] = totalRadiationIntensity;
			sample.values[28] = ultravioletRadiationIntensity;
			sample.values[29] = o3Concentration;
			sample.values[30] = coConcentration;
			sample.values[31] = so2Concentration;
			sample.values[32] = no2Concentration;

			for (UInt i = 0; i < spots.size(); i++)
			{
				sample.values[64 + 2 * i] = (UInt)spots[i].spotType;
				sample.values[64 + 2 * i + 1] = spots[i].distance;
			}

			sample.significantCount = sample.values.size();
			return sample;
		}

		Bool fromGeneralSample(String protocol, SessionGeneralSample sample)
		{
			timestamp = sample.timestamp;

			if (protocol == "env-sample-v3")
			{
				if (sample.values.size() < 2) return FALSE;

				UInt roadNameBinSize = (UInt)sample.values[0].value;
				UInt spotsCount = (UInt)sample.values[1].value;
				if (sample.values.size() != 64 + spotsCount * 2 + roadNameBinSize) return FALSE;

				weatherType = sample.values[2].valid ? (WeatherType)(Int)sample.values[2].value : WT_Unknown;
				temperature = sample.values[3];
				pm25 = sample.values[4];
				speedLimit = sample.values[5].valid ? (Int)sample.values[5].value : OptionalInt();
				roadType = sample.values[6].valid ? (RoadType)(Int)sample.values[6].value : RT_Unknown;
				illumination = sample.values[7];
				windSpeed = sample.values[8];
				roadTraffic = sample.values[9].valid ? (TrafficStatus)(Int)sample.values[9].value : TF_Unknown;
				pressure = sample.values[11];
				humidity = sample.values[12];
				visibility = sample.values[13];
				windDirection = sample.values[14].valid ? (WindDirection)(Int)sample.values[14].value : WD_Unknown;
				pm10 = sample.values[15];
				roadSurfaceStatus = sample.values[16].valid ? (RoadSurfaceStatus)(Int)sample.values[16].value : RSS_Unknown;
				currentLaneTraffic = sample.values[17].valid ? (TrafficStatus)(Int)sample.values[17].value : TF_Unknown;
				leftLaneTraffic = sample.values[18].valid ? (TrafficStatus)(Int)sample.values[18].value : TF_Unknown;
				rightLaneTraffic = sample.values[19].valid ? (TrafficStatus)(Int)sample.values[19].value : TF_Unknown;
				typhoonStatus = sample.values[20].valid ? (DisasterStatus)(Int)sample.values[20].value : DS_Unknown;
				tornadoStatus = sample.values[21].valid ? (DisasterStatus)(Int)sample.values[21].value : DS_Unknown;
				lightningStatus = sample.values[22].valid ? (DisasterStatus)(Int)sample.values[22].value : DS_Unknown;
				hailStatus = sample.values[23].valid ? (DisasterStatus)(Int)sample.values[23].value : DS_Unknown;
				rainfall = sample.values[24];
				cumulativeRainfall = sample.values[25];
				accurateWindDirection = sample.values[26];
				totalRadiationIntensity = sample.values[27];
				ultravioletRadiationIntensity = sample.values[28];
				o3Concentration = sample.values[29];
				coConcentration = sample.values[30];
				so2Concentration = sample.values[31];
				no2Concentration = sample.values[32];

				spots = Array<Spot>(spotsCount);
				for (UInt i = 0; i < spots.size(); i++)
				{
					spots[i].spotType = (SpotType)(Int)sample.values[64 + 2 * i].value;
					spots[i].distance = sample.values[64 + 2 * i + 1].value;
				}

				if (roadNameBinSize > 0)
				{
					Binary roadNameBin(roadNameBinSize);
					for (UInt i = 0; i < roadNameBinSize; i++)
					{
						roadNameBin[i] = (Byte)sample.values[64 + spots.size() * 2 + i].value;
					}
					roadName = roadNameBin;
				}
				else roadName = String();

				if (roadName.isEmpty() && sample.values[10].valid && sample.values[10].isText) roadName = sample.values[10].text;

				return TRUE;
			}
			else if (protocol == "env-sample-v2")
			{
				if (sample.values.size() < 2) return FALSE;

				UInt roadNameBinSize = (UInt)sample.values[0].value;
				UInt spotsCount = (UInt)sample.values[1].value;
				if (sample.values.size() != 10 + spotsCount * 2 + roadNameBinSize) return FALSE;

				weatherType = sample.values[2].valid ? (WeatherType)(Int)sample.values[2].value : WT_Unknown;
				temperature = sample.values[3];
				pm25 = sample.values[4];
				speedLimit = sample.values[5].valid ? (Int)sample.values[5].value : OptionalInt();
				roadType = sample.values[6].valid ? (RoadType)(Int)sample.values[6].value : RT_Unknown;
				illumination = sample.values[7];
				windSpeed = sample.values[8];
				roadTraffic = sample.values[9].valid ? (TrafficStatus)(Int)sample.values[9].value : TF_Unknown;

				spots = Array<Spot>(spotsCount);
				for (UInt i = 0; i < spots.size(); i++)
				{
					spots[i].spotType = (SpotType)(Int)sample.values[10 + 2 * i].value;
					spots[i].distance = sample.values[10 + 2 * i + 1].value;
				}

				if (roadNameBinSize > 0)
				{
					Binary roadNameBin(roadNameBinSize);
					for (UInt i = 0; i < roadNameBinSize; i++)
					{
						roadNameBin[i] = (Byte)sample.values[10 + spots.size() * 2 + i].value;
					}
					roadName = roadNameBin;
				}
				else roadName = String();

				return TRUE;
			}

			return FALSE;
		}

		static EnvSample interpolate(EnvSample& s1, Double w1, EnvSample& s2, Double w2, FullTimestamp timestamp)
		{
			return EnvSample();
		}
	};
}

#endif
